package client;

import shared.*;
import static java.nio.file.FileVisitResult.CONTINUE;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import static java.nio.file.FileVisitResult.*;
import static java.nio.file.StandardWatchEventKinds.*;

public class Toolkit  
{
	//singleton
	private static final Toolkit instance = new Toolkit();
	private Toolkit(){}
	public static Toolkit getInstance(){
		return instance;
	}
	
	private HashMap<String, FileDescriptor> traversedFiles;
	public void traverse (String homeDir)
	{
		traversedFiles = new HashMap<String, FileDescriptor>();
		Path startingDir = Paths.get(homeDir);
		FileTraverser t = new FileTraverser();
		EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
		try 
		{
			Files.walkFileTree(startingDir, opts, Integer.MAX_VALUE, t);
		} 
		catch (IOException e) 
		{
			// TODO Auto-generated catch block
			System.err.println("failed to traverse");
			e.printStackTrace();
		}
	}
	
	
	
	public HashMap<String, FileDescriptor> getTraversedFiles() 
	{
		if (this.traversedFiles == null){
			this.traverse(Configuration.getHomePath().toString());
		}
		return traversedFiles;
	}

	
	
	@Deprecated
	public String computeHash(String pathName) 
	{
		byte[] hashedStream = null;
		byte[] buf          = new byte[1024];
		FileInputStream originalFile = null;
		BufferedInputStream file     = null;
		
		int bytesRead;
		try {
			originalFile = new FileInputStream(pathName);
			file         = new BufferedInputStream(originalFile);
			
			MessageDigest hasher = MessageDigest.getInstance(Constants.HASH_METHOD);

			while (true) {
				bytesRead = file.read(buf);
				if (bytesRead < 0) {
					break;
				}
				hasher.update(buf, 0, bytesRead);
			}
			
			file.close();
			hashedStream = hasher.digest();

		} 
		catch (Exception e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		String hash = "";

		for (int i = 0; i < hashedStream.length; i++) {
			hash += Integer.toString(
					(hashedStream[i] & 0xff) + 0x100, 16).substring(1);
		}
		return hash;
	}
	
	
	private class FileTraverser extends SimpleFileVisitor<Path>
	{
		
		@Override
		public FileVisitResult visitFile(Path file, BasicFileAttributes attr) 
		{
			//System.out.format("Regular file: %s ", file);
			//System.out.println("(" + attr.size() + "bytes)");
			
			Path homePath = Configuration.getHomePath();
			file = file.toAbsolutePath();
			String relativePath = homePath.relativize(file).toString();

			
			FileDescriptor fileDescriptor = new FileDescriptor();
			fileDescriptor.setPathname(relativePath);

			
			traversedFiles.put(relativePath, fileDescriptor);
			return CONTINUE;
		}

	
		// Print each directory visited.
		@Override
		public FileVisitResult postVisitDirectory(Path dir, IOException exc) 
		{
			if (dir.equals(Configuration.getHomePath())){
				return CONTINUE;
			}
			
			//System.out.format("Directory: %s%n", dir);
			
			Path homePath = Configuration.getHomePath();
			dir = dir.toAbsolutePath();
			String relativePath = homePath.relativize(dir).toString();
			
			
			FileDescriptor fileDescriptor = new FileDescriptor();
			fileDescriptor.setPathname(relativePath.toString());
			fileDescriptor.setDirectory(true);
			traversedFiles.put(relativePath, fileDescriptor);
			
			// Add to folder watcher service
			try
			{
			    // TODO: what if the directory has been registered before?
			    dir.register(Configuration.getFolderWatcher(), ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
			} 
			catch (IOException e)
			{
			    System.err.println("Cannot register folder to folder watcher service");
			}
			
			return CONTINUE;
		}

		
		
		@Override
		public FileVisitResult visitFileFailed(Path file, IOException exc)
		{
			System.err.println(exc);
			return CONTINUE;
		}

	}
	
	
	/**
	 * @param Take two hash maps as input
	 * @return A hash table where key is the union of two input hash tables and value is a char[] where first character
	 * is the key existence in the first table.
	 */
	public HashMap<String, char[]> 
	mergeKey(HashMap<String, FileDescriptor> map1, HashMap<String, FileDescriptor> map2){
		HashMap<String, char[]> occurMap = new HashMap<String, char[]> ();
		for (String key: map1.keySet()){
			char[] c = {'1','0'};
			occurMap.put(key, c);
		}
		
		for (String key: map2.keySet()){
			if (occurMap.get(key)==null){
				//key didn't occur in map 1
				char[] c = {'0','1'};
				occurMap.put(key, c);
			}
			else{
				char[] c = occurMap.get(key);
				c[1] = '1';
				occurMap.put(key, c);
 			}
		}
		
		return occurMap;
	}
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		HashMap<String, FileDescriptor> map1 = new HashMap<String, FileDescriptor> ();
		HashMap<String, FileDescriptor> map2 = new HashMap<String, FileDescriptor> ();
		FileDescriptor fd = new FileDescriptor();
		map1.put("file1", fd);
		map1.put("file2", fd);
		map2.put("file2", fd);
		map2.put("file3", fd);
		
		HashMap<String, char[]> m = Toolkit.getInstance().mergeKey(map1, map2);
		for (String key: m.keySet()){
			System.out.println(key+"="+ new String (m.get(key)));
		}
		
	}


}
